package edu.nyu.hps.New;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

public class Computation {

	People p[];
	Hospital h[];
	Ambulance a[];
	int anyOneAlive, numP, numH, instant, totalSaved, bestSaved;
	
	List<String> routes = new ArrayList<String>();
	

	/**
	 * Constructor to initialise objects
	 * 
	 * @param dataForPeople
	 * @param dataForHospital
	 */
	public Computation(List<String> dataForPeople, List<String> dataForHospital) {
		
		this.numP = dataForPeople.size();
		this.numH = dataForHospital.size();
		instant = 0;
		anyOneAlive = dataForPeople.size();
		this.totalSaved = 0;
		this.bestSaved = 0;
		p = new People[dataForPeople.size()];
		h = new Hospital[dataForHospital.size()];

		for (int i = 0; i < dataForPeople.size(); i++) {
			String[] arr = dataForPeople.get(i).split(",");
			int x = Integer.parseInt(arr[0]);
			int y = Integer.parseInt(arr[1]);
			int rescueTime = Integer.parseInt(arr[2]);
			int index = i;
			People pt = new People(x, y, rescueTime, index);
			p[i] = pt;

		}

		for (int i = 0; i < dataForHospital.size(); i++) {
			String[] arr = dataForHospital.get(i).split(",");
			int x = Integer.parseInt(arr[0]);
			int y = Integer.parseInt(arr[1]);
			int maxAmbulance = Integer.parseInt(arr[2]);
			int index = i;
			Hospital ht = new Hospital(x, y, maxAmbulance, index);
			h[i] = ht;
		}

		List<Ambulance> ambulanceArray = new ArrayList<Ambulance>();
		for (int i = 0; i < h.length; i++) {
			for (int j = 0; j < h[i].maxAmbulance; j++) {
				Ambulance at = new Ambulance(h[i]);
				ambulanceArray.add(at);
			}
		}

		a = new Ambulance[ambulanceArray.size()];
		for (int i = 0; i < ambulanceArray.size(); i++) {
			a[i] = ambulanceArray.get(i);
		}

		// Calculate the nearest hospital for each person
		for (int i = 0; i < numP; i++) {
			int dist = 100;
			for (int j = 0; j < numH; j++) {
				int temp = distBetHnP(j, i);
				if (temp < dist) {
					dist = temp;
					p[i].h = h[j];
				}
			}
		}
	}

	/**
	 * Calculates distance between hospital i and person j
	 * 
	 * @param i
	 * @param j
	 * @return distance
	 */
	public int distBetHnP(int i, int j) {
		return (Math.abs(h[i].x - p[j].x) + Math.abs(h[i].y - p[j].y));
	}

	/**
	 * Calculates distance between person i and person j
	 * 
	 * @param i
	 * @param j
	 * @return
	 */
	public int distBetPnP(int i, int j) {
		return (Math.abs(p[i].x - p[j].x) + Math.abs(p[i].y - p[j].y));
	}

	/**
	 * Determines if the person i and j can be saved in the whole journey
	 * 
	 * @param i
	 * @param j
	 * @param journey
	 * @return true if the people can be saved
	 */
	public boolean canSave(int i, int j, int journey) {
		if (journey <= p[i].rescueTime && journey <= p[j].rescueTime) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Determines if a person i can be saved within the journey
	 * 
	 * @param i
	 * @param journey
	 * @return
	 */
	public boolean canSave(int i, int journey) {
		if (journey <= p[i].rescueTime) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Updates the density of people around the hospital
	 */
	public void updateDensityOfHospital() {
		for (int i = 0; i < h.length; i++) {
			h[i].density = 0;
		}
		for (int i = 0; i < p.length; i++) {
			if (!p[i].isSaved) {
				h[p[i].h.index].density++;
			}
		}

//		List<People> peopleArray = Arrays.asList(p);
//		Collections.sort(peopleArray);
//
//		for (int i = 0; i < numP; i++) {
//			p[i] = peopleArray.get(i);
//		}

		List<Ambulance> ambArray = Arrays.asList(a);
		Collections.sort(ambArray);
		for (int i = 0; i < ambArray.size(); i++) {
			a[i] = ambArray.get(i);
		}
	}

	/**
	 * Updates rescue time for each patient, each time
	 */
	public void reduceRescueTime() {
		anyOneAlive = 0;
		for (int i = 0; i < numP; i++) {
			if (p[i].rescueTime > 0) {
				p[i].rescueTime -= 1;
				anyOneAlive++;
			}
		}
	}
	
	/**
	 * Reset data of successive runs
	 */
	public void reset(){
		instant = 0;
		anyOneAlive = numP;
		totalSaved = 0;
		routes.clear();
		
		//Resets for the hospital data
		for(int i=0;i<numH;i++){
			h[i].casualties=0;
		}
		
		//Resets for the people data
		for(int i=0;i<numP;i++){
			if(p[i].isSaved){
				h[p[i].h.index].casualties++;
				p[i].timesSurvived+=1;
				p[i].isSaved=false;
			}
			p[i].rescueTime = p[i].origRescueTime;
		}
		
		List<People> peopleArray = Arrays.asList(p);
		Collections.sort(peopleArray);

		for (int i = 0; i < numP; i++) {
			p[i] = peopleArray.get(i);
		}
		
		//Resets for the ambulance data
		for (int i = 0; i < a.length; i++) {
			a[i].time=0;
			a[i].h = a[i].origH;
		}
	}

	/**
	 * The actual computation that saves victims
	 */
	public void startSaving() {
		reset();
		updateDensityOfHospital();
		while (anyOneAlive != 0) {
			for (int i = 0; i < a.length; i++) {
				if (a[i].time <= instant) {
					for (int j = 0; j < numP; j++) {
						if (!p[j].isSaved) {
							if(p[j].h==a[i].h){
								int distance1 = distBetHnP(a[i].h.index, j)
										+ distBetHnP(p[j].h.index, j) + 2;
								if (canSave(j, distance1)) {
									for (int k = 0; k < numP; k++) {
										if (!p[k].isSaved && k != j
												&& !p[j].isSaved) {
											int distance2 = distBetHnP(
													a[i].h.index, j)
													+ distBetPnP(j, k)
													+ distBetHnP(p[k].h.index, k)
													+ 3;
											if (canSave(j, k, distance2)) {
												p[k].isSaved = true;
												p[j].isSaved = true;
												routes.add(// instant + " "
														a[i].h.index + " "
																+ p[j].index + " "
																+ p[k].index + " "
																+ p[k].h.index);// +
																				// " "
												// + distance2 + " " + i);
												a[i].time += distance2;
												a[i].h = h[p[k].h.index];
												totalSaved++;
												updateDensityOfHospital();
												break;
											}
										}
									}
									if (!p[j].isSaved) {
										p[j].isSaved = true;
										routes.add(// instant + " " +
												a[i].h.index + " " + p[j].index
														+ " " + p[j].h.index);// +
																				// " "
																				// +
																				// distance1
										// + " " + i);
										a[i].time += distance1;
										a[i].h = h[p[j].h.index];
										updateDensityOfHospital();
										totalSaved++;
									}
									break;
								}
							}
						}
					}
				}
			}
			instant++;
			reduceRescueTime();
		}
	}
}
